#pragma once
#include <sqlite3.h>
#include <iostream>
#include <memory>
#include <vector>

#include <random>       // random_device mt19937_64 uniform_int_distribution
#include <sstream>      // std::stringstream
#include <iomanip>      // std::setw()
#include <atomic>

#include <sys/stat.h>
#include <fstream>
#include <cerrno>
#include <cstring>

#include "log.hpp"



namespace xzt
{
    class SqliteHelper
    {
        // 参数说明：
        // void* arg: 传递给 sqlite3_exec 的第四个参数
        // int column_count: 当前结果行中的列数
        // char** column_values: 包含当前结果行中每一列的值的数组
        // char** column_names: 包含当前结果行中每一列的列名的数组
        typedef int (*SqliteCallback)(void *, int, char **, char **);

    public:
        SqliteHelper(const std::string &dbfile) : _dbfile(dbfile), _handler(nullptr) {}

        // 封装sqlite3_open_v2(const char* filename, sqlite3** ppDb, int flags, const char* zVfs);
        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler,
                                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);

            if (ret != SQLITE_OK)
            {
                ELOG("创建/打开sqlite数据库失败：\n");
                ELOG("\t%s\n", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }

        // 封装sqlite3_exec(sqlite3*, char* sql,
        //         int(*callback)(void*, int, char**, char**),
        //         void* arg, char** err)
        // 参数arg: 传递给cb回调函数的第一个参数
        bool exec(const std::string &sql, SqliteCallback cb, void *arg)
        {
            std::unique_ptr<char[]> errmsg(new char[128]);
            char *errmsg_ptr = errmsg.get();
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, &errmsg_ptr);

            if (ret != SQLITE_OK)
            {
                ELOG("%s "
                     "执行语句失败：\n",
                     sql.c_str());
                ELOG("\t%s", errmsg_ptr);
                // std::cout << sqlite3_errmsg(_handler) << std::endl;
                return false;
            }
            return true;
        }

        // 封装sqlite_close_v2(sqlite3*)
        void close()
        {
            if (_handler)
            {
                sqlite3_close_v2(_handler);
            }
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };

    class StringHelper
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            //   ...news.#.haha.aah.#.    // sep = "."
            // index^   ^pos    ====> 提取子串"news"将其push进result
            size_t pos = 0, index = 0;
            while (index < str.size())
            {
                pos = str.find(sep, index);
                // 如果未找到sep分隔符，取整个字符串push进result容器
                if (pos == std::string::npos)
                {
                    result.push_back(str.substr(index));
                    break;
                }

                if (pos == index)
                {
                    index = pos + sep.size();
                    continue;
                }

                // 若截取到新子串，push进result容器，同时index右移到pos位置后（pos + sep.size()）
                result.push_back(str.substr(index, pos - index));
                index = pos + sep.size();
            }
            return result.size();
        }
    };

    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            // 1.利用random_device生成机器随机数，但效率较低
            std::random_device rd;
            // 2.利用机器随机数，通过梅森旋转算法，生成一个伪随机数
            std::mt19937_64 generator(rd());
            // 3.生成指定范围（0-255）之间的伪随机数
            std::uniform_int_distribution<int> distribution(0, 255);
            // 4.将制定范围随机数转为16进制，存储在字符串中
            // 5.由于0-255转16进制后对应位数为2，所以生成16位十六进制需要重复8次
            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                // setw(2): 设置位宽为2     setfill(char c): 用c字符填充位宽可能的空位
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
                if(i == 3 || i == 5 || i == 7)
                {
                    ss << "-";
                }
            }

            // 6.追加后16位的序号
            static std::atomic<size_t> seq(1);
            size_t num = seq.fetch_add(1);
            for(int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i*8)) & 0xff);
                if(i == 6)
                {
                    ss << "-";
                }
            }

            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string& filename): _filename(filename) {}

        bool exist()
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0;
        }

        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if(ret < 0)
            {
                ELOG("FileHelper::size() -> stat() error occurred");
                return 0;
            }
            return st.st_size;
        }

        // 参数说明：body是输出型参数，返回读取到的字符串；
        //          offset 偏移量；
        //          len 读取长度
        bool read(char* body, size_t offset, size_t len)
        {
            // 1.打开文件
            std::ifstream ifs;
            ifs.open(_filename.c_str(), std::ios::binary | std::ios::in);
            if(ifs.is_open() == false)
            {
                ELOG("FileHelper::read() -> [%s]文件打开失败", _filename.c_str());
                return false;
            }

            // 2.跳转文件读写位置
            ifs.seekg(offset, std::ios::beg);    // 从ios::beg位置为起点，偏移量为offset

            // 3.读取文件数据
            ifs.read(body, len);
            if(ifs.good() == false)
            {
                ELOG("FileHelper::read() -> [%s]文件读取失败", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4.关闭文件
            ifs.close();
            return true;
        }

        bool read(std::string& body)
        {
            size_t file_size = this->size();
            body.resize(file_size);
            return read(&(body[0]), 0, file_size);
        } 

        bool write(const char* body, size_t offset, size_t len)
        {
            // 1.打开文件
            std::fstream fs;
            fs.open(_filename.c_str(), std::ios::binary | std::ios::in | std::ios::out);
            if(fs.is_open() == false)
            {
                ELOG("FileHelper::write() -> [%s]文件打开失败", _filename.c_str());
                return false;
            }

            // 2.跳转文件读写位置
            fs.seekp(offset, std::ios::beg);    // 从ios::beg位置为起点，偏移量为offset

            // 3.读取文件数据
            fs.write(body, len);
            if(fs.good() == false)
            {
                ELOG("FileHelper::write() -> [%s]文件读取失败", _filename.c_str());
                fs.close();
                return false;
            }
            // 4.关闭文件
            fs.close();
            return true;
        }

        bool write(const std::string& body)
        {
            return write(body.c_str(), 0, body.size());
        }

        bool rename(const std::string& new_name)
        {
            return (::rename(_filename.c_str(), new_name.c_str()) == 0);
        }

        static bool createFile(const std::string& filename)
        {
            std::ofstream fs;
            fs.open(filename.c_str(), std::ios::binary | std::ios::out);
            if(fs.is_open() == false)
            {
                ELOG("FileHelper::createFile() -> [%s]文件创建失败", filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }

        static bool removeFile(const std::string& filename)
        {
            return (::remove(filename.c_str()) == 0 || errno == ENOENT);
        }

        static std::string parentDirectory(const std::string& path_filename)
        {
            size_t pos = path_filename.find_last_of("/");
            if(pos == std::string::npos)    // path_filename中不包含'/'符号，说明仅有文件名
            {
                return "./";
            }

            std::string ppath = path_filename.substr(0, pos);
            return ppath;
        }

        static bool createDirectory(const std::string& path)
        {
            int pos = 0, index = 0;
            while(pos < path.size())
            {
                pos = path.find("/", index);    // 从index开始查找"\"，要继续向右查询仅需更新index的值
                
                if(pos == std::string::npos)    // 达到最子目录对应字符串
                {
                    bool ret = mkdir(path.c_str(), 0775) == 0;
                    if(ret == false && errno != EEXIST)  // 只有返回-1表示创建当前目录失败同时原因不是因为当前路径已存在，才报错)
                    {
                        ELOG("FileHelper::createDirectory() -> [%s]目录创建失败<%s>", path.c_str(), strerror(errno));
                        return false;
                    }
                    return true;
                }
                // 创建父路径目录
                std::string sub_parent_s = path.substr(0, pos);
                if(mkdir(sub_parent_s.c_str(), 0775) < 0 && errno != EEXIST)  // 只有返回-1表示创建当前目录失败同时原因不是因为当前路径已存在，才报错
                {
                    ELOG("FileHelper::createDirectory() -> [%s]目录创建失败<%s>", sub_parent_s.c_str(), strerror(errno));
                    return false;
                }

                index = pos + 1;
            }
            return true;
        }

        static bool removeDirectory(const std::string& path)
        {
            std::string order = "rm -rf " + path;
            return (system(order.c_str()) != -1);
        }

    private:
        std::string _filename;
    };
};
